﻿using CustomPixelShaderEffect.UlitFolder;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.Graphics.Canvas;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage.Streams;
using Windows.Storage;
using System.Numerics;
using CustomPixelShaderEffect.AttributeUsageFloder;
using System.Xml.Linq;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.UI;
using Windows.UI;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace CustomPixelShaderEffect.views
{
    [PageTypeAttribute("颜色替换")]
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class ColorReplacementPage : Page
    {
        PixelShaderEffect effect;
        //选区效果
        PixelShaderEffect constEffect;
        CanvasRenderTarget constRender;
        CanvasBitmap bitmap;
        Color[] bitmapColors;
        public ColorReplacementPage()
        {
            this.InitializeComponent();
            Init();
            InitOperate();
        }
        Hsv targetHsv = new Hsv();
        Hsv sourceHsv = new Hsv();
        Color? color;
        float effectWidth;
        float effectHeight;
        float previewWidth;
        float previewHeight;
        //记录鼠标是否在画布上按下
        bool isPressed;
        //记录小图在画布上的大小和位置，和偏移
        float offsetX, offsetY, scale, width, height;
        Vector2 pos;
        void Init()
        {
            canvas.CreateResources += async (s, e) =>
            {
                //获取着色器二进制文件
                StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Shaders/ColorReplacementEffect.bin"));
                IBuffer buffer = await FileIO.ReadBufferAsync(file);
                //转换成字节数组
                var bytes = buffer.ToArray();
                //用 字节数组 初始化一个 PixelShaderEffect 对象；b v
                effect = new PixelShaderEffect(bytes);

                file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Shaders/ConstituencyEffect.bin"));
                buffer = await FileIO.ReadBufferAsync(file);
                bytes = buffer.ToArray();
                constEffect = new PixelShaderEffect(bytes);
            };
            //选择图片
            selectPicture.Click += async (s, e) =>
            {
                var file = await Ulit.SelectFileAsync(new List<string> { ".png", ".jpg" });
                if (file == null)
                {
                    bitmap = null;
                    color = null;
                }
                else
                {
                    bitmap = await CanvasBitmap.LoadAsync(canvas.Device, await file.OpenAsync(FileAccessMode.Read));
                    //获取图像像素数组
                    bitmapColors = bitmap.GetPixelColors();
                    constRender = new CanvasRenderTarget(canvas, bitmap.Size);
                }
                CalcuationDrawPosition();
                canvas.Invalidate();
            };
            canvas.Draw += (s, e) =>
            {
                //绘制黑白网格
                Win2dUlit.DrawGridGraphics(e.DrawingSession, 100);
                //判断effect 和位图是否为空
                if (effect == null || bitmap == null)
                    return;
                if (color != null)
                {
                    //绘制替换颜色效果
                    //颜色将0~255 转换成0~1 因为hlsl里面的颜色是0~1范围的并且是float类型，所以这里需要将每个颜色的分量除以255
                    Vector3 hsv = targetHsv.HsvToVector3();
                    Vector3 sourceColor = new Vector3(color.Value.R / 255f, color.Value.G / 255f, color.Value.B / 255f);
                    //通过键值对的形式设置属性传递到着色器里面
                    effect.Properties["threshold"] = (float)threshold.Value;
                    effect.Properties["hsv"] = hsv;
                    effect.Properties["sourceColor"] = sourceColor;
                    effect.Source1 = bitmap;
                    //绘制效果图
                    var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
                    effectTran.Source = effect;
                    e.DrawingSession.DrawImage(effectTran);
                    //绘制选区图 右下角
                    var constTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
                    constEffect.Source1 = bitmap;
                    constEffect.Properties["threshold"] = (float)threshold.Value;
                    constEffect.Properties["color"] = sourceColor;
                    using (var ds = constRender.CreateDrawingSession())
                    {
                        ds.Clear(Colors.Transparent);
                        ds.DrawImage(constEffect, new Rect(new Point(), bitmap.Size), new Rect(new Point(), bitmap.Size));
                    }
                    constTran.Source = constRender;
                    e.DrawingSession.DrawImage(constTran, previewWidth, effectHeight);
                }
                else
                {
                    var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
                    effectTran.Source = bitmap;
                    e.DrawingSession.DrawImage(effectTran);
                }
                //绘制原图 左下角
                var previewTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
                previewTran.Source = bitmap;
                e.DrawingSession.DrawImage(previewTran, 0, effectHeight);
                //绘制选择颜色缩略图
                if (isPressed && (pos.X >= offsetX && pos.X <= offsetX + width && pos.Y >= offsetY && pos.Y <= offsetY + height))
                {
                    //画三角形
                    CanvasPathBuilder builder = new CanvasPathBuilder(canvas);
                    builder.BeginFigure(pos);
                    builder.AddLine(pos - new Vector2(5, 5));
                    builder.AddLine(pos - new Vector2(-5, 5));
                    builder.EndFigure(CanvasFigureLoop.Closed);
                    CanvasGeometry geometry = CanvasGeometry.CreatePath(builder);
                    e.DrawingSession.FillGeometry(geometry, Colors.White);
                    e.DrawingSession.FillRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.White);
                    //采样位置
                    var usePx = (pos.X - offsetX) / scale;
                    var usePy = (pos.Y - offsetY) / scale;
                    e.DrawingSession.DrawImage(bitmap, pos.X - 50, pos.Y - 100 - 5, new Rect(usePx - 50, usePy - 50, 100, 100));
                    e.DrawingSession.DrawRectangle(new Rect(pos.X - 1, pos.Y - 56, 3, 3), Colors.Orange);
                    e.DrawingSession.DrawRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.Black);
                }
            };
            threshold.ValueChanged += (s, e) => canvas.Invalidate();
            RangeBaseValueChangedEventHandler hsvChanged = (s, e) =>
            {
                if (color == null)
                    return;

                if (s is Slider slider)
                {
                    switch (slider.Name)
                    {
                        case "hue":
                            targetHsv.H = e.NewValue;
                            break;
                        case "saturation":
                            targetHsv.S = e.NewValue;
                            break;
                        case "brightness":
                            targetHsv.V = e.NewValue;
                            break;
                    }
                    replaceColor.Fill = new SolidColorBrush((sourceHsv + targetHsv).HsvToRgb());
                }
                canvas.Invalidate();
            };
            hue.ValueChanged += hsvChanged;
            saturation.ValueChanged += hsvChanged;
            brightness.ValueChanged += hsvChanged;
        }

        void InitOperate()
        {
            //画布大小更改，计算绘制区域
            canvas.SizeChanged += (s, e) => CalcuationDrawPosition();
            //鼠标再画布按下
            canvas.PointerPressed += (s, e) =>
            {
                isPressed = true;
                canvas.Invalidate();
            };
            //鼠标松开
            canvas.PointerReleased += (s, e) =>
            {
                isPressed = false;
                canvas.Invalidate();
            };

            canvas.PointerMoved += (s, e) =>
            {
                if (!isPressed || bitmap == null)
                    return;
                Point p = e.GetCurrentPoint(canvas).Position;
                pos = p.ToVector2();
                //是否进入范围
                if ((p.X >= offsetX && p.X <= offsetX + width && p.Y >= offsetY && p.Y <= offsetY + height))
                {
                    var x = p.X - offsetX;
                    var y = p.Y - offsetY;
                    x = x / scale;
                    y = y / scale;
                    color = bitmapColors[(int)x + (int)y * bitmap.SizeInPixels.Width];
                    sourceHsv = sourceHsv.RgbToHsv(color.Value);
                    replaceColor.Fill = new SolidColorBrush((sourceHsv + targetHsv).HsvToRgb());
                    //设置选中的颜色
                    selectColor.Background = new SolidColorBrush(color.Value);
                    canvas.Invalidate();
                }
            };
        }
        void CalcuationDrawPosition()
        {
            //判断effect 和位图是否为空
            if (effect == null || bitmap == null)
                return;
            var element = canvas;
            effectWidth = (float)element.ActualWidth;
            effectHeight = (float)element.ActualHeight * 0.7f;
            previewWidth = effectWidth*0.5f;
            previewHeight = (float)element.ActualHeight * 0.3f;
            var mat = Win2dUlit.CalcutateImageCenteredMat(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
            scale = mat.M11;
            offsetX = mat.M31;
            offsetY = mat.M32 + effectHeight;
            width = (float)(scale * bitmap.Size.Width);
            height = (float)(scale * bitmap.Size.Height);
        }
    }
}
